#!/usr/bin/python
# -*- coding: utf-8 -*-

__author__ = 'Yujie Xiao'
__update_time__ = '2021-03-25'
__version__ = '1.0.0'

import sys
if sys.version_info.major == 2:
    import SocketServer
    from SocketServer import StreamRequestHandler as SRH
else:
    import socketserver as SocketServer
    from socketserver import StreamRequestHandler as SRH
import threading
import json
import time
from . import config

# recv_json '{"type": $type, "content": {}, 'time': $time[, "pg_action": [$pg_action[, $table_name[, $column_name] ] ] ] }\n'
# send_json '{"error": 0/1[, "type": $type, "content": {}, 'time': $time] }\n'

def socket_error_msg(msg):
    if msg[0] == 10061:
        print('Cannot Connect Service')
    elif msg[0] == 10054:
        print('Connection Break')
    elif msg[0] == 9:
        print('\nkeep connection subprocess break')
    elif msg[0] == 104:
        print('Connection reset by peer')
    else:
        print('Unkown error')
    print("Creating Socket Failure. Error Code : " + str(msg[0]) + " Message : " + msg[1] + '\n')

class Servers(SRH):
    sort_dict_lock = threading.Lock()
    dispatch_dict_lock = threading.Lock()
    extension_dict_lock = threading.Lock()
    aom_main_dict = {}
    plan_dispatch_dict = {}
    extension_client_dict = {}
    client_type = ''

    def sendall_len(self, data, sock=''):
        data = '%06d' % len(data) + data
        if type(str()) != type(bytes()):
            data = data.encode()
        if sock:
            sock.sendall(data)
        else:
            self.request.sendall(data)

    def heartbeat(self):
        while True:
            try:
                self.sendall_len('10000\n')
            except SocketServer.socket.error as msg:
                print(self.request.getpeername(),)
                socket_error_msg(msg)
                self.request.close()
                break
            time.sleep(90)

    def del_dict(self, client_type, *del_list, **kwargs):
        mode = kwargs['mode'] if 'mode' in kwargs else 'non-self'
        for i in del_list:
            if client_type == 'aom_main' and mode != 'self' or client_type == 'plan_dispatch' and mode == 'self':
                self.dispatch_dict_lock.acquire()
                if i in self.plan_dispatch_dict:
                    del self.plan_dispatch_dict[i]
                    print('del ', i, 'from plan_dispatch_list')
                self.dispatch_dict_lock.release()
            elif client_type == 'plan_dispatch' and mode != 'self' or client_type == 'aom_main' and mode == 'self':
                self.sort_dict_lock.acquire()
                if i in self.aom_main_dict:
                    del self.aom_main_dict[i]
                    print('del ', i, 'from aom_main_list')
                self.sort_dict_lock.release()
            elif client_type == 'extension_client':
                self.extension_dict_lock.acquire()
                if i in self.extension_client_dict:
                    del self.extension_client_dict[i]
                    print('del ', i, 'from extension_client_list')
                self.extension_dict_lock.release()
            self.extension_send_process(i + ' is offline.')   

    def extension_send_process(self, data):
        _send_dict = self.extension_client_dict
        if _send_dict:
            _remove_list = []
            for i in _send_dict:
                try:
                    
                    Online_client = {'aom_main': self.aom_main_dict.keys(), 'plan_dispatch': self.plan_dispatch_dict.keys()}
                    self.sendall_len(json.dumps(Online_client) + '\n', _send_dict[i])
                    self.sendall_len(json.dumps(data) + '\n', _send_dict[i])
                except SocketServer.socket.error as msg:
                    print(i,)
                    socket_error_msg(msg)
                    _remove_list.append(i)
            if _remove_list:
                self.del_dict('extension_client', *_remove_list)

    def response_process(self):
        while True:
            _remove_list = []
            send_flag = False
            bufsize = self.request.recv(6)
            if len(bufsize) == 0:
                print('Connection Break: ', str(self.client_address))
                break
            bufsize = int(bufsize)
            recv_data = self.request.recv(bufsize)
            data = json.loads(recv_data)
            print(data)
            self.client_type = data['type']
            client_type = data['type']

            if client_type in config.support_client_type:
                if client_type == 'aom_main' or client_type == 'plan_dispatch' or client_type == 'object_generator':
                    if client_type == 'aom_main':
                        if str(self.client_address) not in self.aom_main_dict:
                            self.aom_main_dict[str(self.client_address)] = self.request
                            print('aom_main: ', self.aom_main_dict.keys())
                        _send_dict = self.plan_dispatch_dict
                    else:
                        if client_type == 'plan_dispatch':
                            if str(self.client_address) not in self.plan_dispatch_dict:
                                self.plan_dispatch_dict[str(self.client_address)] = self.request
                                print('plan_dispatch :', self.plan_dispatch_dict.keys())
                                print(data)
                        _send_dict = self.aom_main_dict

                    if 'content' in data:
                        if _send_dict:
                            for i in _send_dict:
                                try:
                                    data.update({'error': 0})
                                    send_data = json.dumps(data)
                                    if client_type == 'object_generator':
                                        print(send_data)
                                        print(_send_dict)
                                    if 'pg_action' in data:
                                        print(send_data)
                                    self.sendall_len(send_data + '\n', _send_dict[i])
                                    send_flag = True
                                    break
                                except SocketServer.socket.error as msg:
                                    print(i)
                                    socket_error_msg(msg)
                                    _remove_list.append(i)

                            if _remove_list:
                                self.del_dict(client_type, *_remove_list)

                        if not send_flag:
                            error_msg = json.dumps({'error':1}) + '\n'
                            self.sendall_len(error_msg)

                    else:
                        self.sendall_len('connection %s:%s at %s succeed!' % (config.host, config.port, time.ctime()))

                elif client_type == 'extension_client':
                    if str(self.client_address) not in self.extension_client_dict:
                        self.extension_client_dict[str(self.client_address)] = self.request
                        _send_dict = self.plan_dispatch_dict

                    if 'content' in data:
                        if _send_dict:
                            for i in _send_dict:
                                try:
                                    data.update({'error': 0})
                                    send_data = json.dumps(data)
                                    if 'pg_action' in data:
                                        print(send_data)
                                    self.sendall_len(send_data + '\n', _send_dict[i])
                                    send_flag = True
                                    break
                                except SocketServer.socket.error as msg:
                                    print(i)
                                    socket_error_msg(msg)
                                    _remove_list.append(i)

                            if _remove_list:
                                self.del_dict(client_type, *_remove_list)

                        if not send_flag:
                            error_msg = json.dumps({'error':1}) + '\n'
                            self.sendall_len(error_msg)

                self.extension_send_process(data)

            else:
                self.sendall_len('unsupported client type: ' + recv_data)
                try:
                    self.request.shutdown(2)
                except OSError as msg:
                    pass
                self.request.close()
                raise KeyError(
                    u'unsupported client type: ', data
                ) 

    def handle(self):
        print('got connection from ',self.client_address)
        
        #a=threading.Thread(target=self.heartbeat)
        #a.start()

        try:
            self.response_process()
        
        except SocketServer.socket.error as msg:
            print(self.client_type)
            print(self.client_address,)
            socket_error_msg(msg)
        except TypeError as msg:
            print(msg, self.client_address, self.request)
        except KeyError as msg:
            print(msg)
        finally:
            self.del_dict(self.client_type, str(self.client_address), mode='self')
            try:
                self.request.shutdown(2)
            except OSError as msg:
                pass
            self.request.close()

def start_server():
    if config.localhost:
        addr = ('127.0.0.1',10086)
        print('start localhost test\n')
    else:
        addr = (config.host, config.port)

    print('server is running....'  )
    server = SocketServer.ThreadingTCPServer(addr,Servers)  
    server.serve_forever()

if __name__ == '__main__':
    start_server()
